[genre].tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import clsx from "clsx";
  2. import Link from "next/link";
  3. import { useContext, useMemo } from "react";
  4. import { useRouter } from "next/router";
  5. import { GetServerSideProps } from "next";
  6. import { get } from "libs/http";
  7. import useGet from "libs/hooks/useGet";
  8. import { Context } from "libs/context";
  9. import NovelItem from "components/NovelItem";
  10. import styles from "styles/genre.module.scss";
  11. import useStore from "libs/hooks/useStore";
  12. import Head from "next/head";
  13. import { SeoHead, SeoHeadConfig } from "components/SeoHead";
  14. const Genre = () => {
  15. const { query } = useRouter();
  16. const { siteConfig } = useStore();
  17. const { data } = useGet<ListItem[]>(
  18. query.genre ? `/api/genre/${query.genre}` : "/api/list"
  19. );
  20. const store = useContext(Context);
  21. const currentName = useMemo(() => {
  22. const item = store.genre.find((item) => item.uri === query.genre);
  23. return item ? item.name : "";
  24. }, [query.genre, store.genre]);
  25. const seoConfig: SeoHeadConfig = useMemo(() => {
  26. return {
  27. title: `${currentName} Novels - ${siteConfig.siteName}`,
  28. description: ``,
  29. keywords: `${[
  30. `${currentName || "All"} stories`,
  31. `${currentName || "All"} novels`,
  32. `read ${currentName || "All"} novels`,
  33. siteConfig.keywords,
  34. siteConfig.siteName,
  35. ].join(", ")}`,
  36. url: `https://${siteConfig.host}/novels${
  37. query.genre ? `/${query.genre}` : ""
  38. }`,
  39. jsonLd: JSON.stringify([
  40. {
  41. "@context": "https://schema.org",
  42. "@type": "BreadcrumbList",
  43. itemListElement: [
  44. {
  45. "@type": "ListItem",
  46. position: 1,
  47. name: "Home",
  48. item: `https://${siteConfig.host}`,
  49. },
  50. {
  51. "@type": "ListItem",
  52. position: 2,
  53. name: "Novels",
  54. item: `https://${siteConfig.host}/novels`,
  55. },
  56. ...(query.genre
  57. ? [
  58. {
  59. "@type": "ListItem",
  60. position: 3,
  61. name: `${currentName} Novels`,
  62. item: `https://${siteConfig.host}/novels/${query.genre}`,
  63. },
  64. ]
  65. : []),
  66. ],
  67. },
  68. {
  69. "@context": "https://schema.org",
  70. "@type": "ItemList",
  71. itemListElement: (data?.data || []).map((item, idx) => ({
  72. "@type": "ListItem",
  73. position: idx + 1,
  74. url: `https://${siteConfig.host}/novel/${item.uri}`,
  75. name: item.name,
  76. image:
  77. "http://img.webnovel.com/bookcover/16709365405930105/600/600.jpg",
  78. author: { "@type": "Person", name: item.author },
  79. publisher: { "@type": "Organization", name: siteConfig.siteName },
  80. })),
  81. },
  82. ...siteConfig.jsonLd,
  83. ]),
  84. };
  85. }, [
  86. currentName,
  87. data?.data,
  88. query.genre,
  89. siteConfig.host,
  90. siteConfig.jsonLd,
  91. siteConfig.keywords,
  92. siteConfig.siteName,
  93. ]);
  94. return (
  95. <main className="container">
  96. <SeoHead seoConfig={seoConfig} />
  97. <h2 className="novel-title">Genres</h2>
  98. <div className={styles.genres}>
  99. <Link
  100. href="/novels"
  101. title="All novels"
  102. className={clsx(styles.genre, {
  103. [styles.current]: !query.genre,
  104. })}
  105. >
  106. All
  107. </Link>
  108. {store.genre.map((item) => (
  109. <Link
  110. href={`/novels/${item.uri}`}
  111. key={item.uri}
  112. title={item.name}
  113. className={clsx(styles.genre, {
  114. [styles.current]: query.genre === item.uri,
  115. })}
  116. >
  117. {item.name}
  118. </Link>
  119. ))}
  120. </div>
  121. <h2 className="novel-title">{`${currentName} Novels`}</h2>
  122. <ul className="novel-list">
  123. {(data?.data || []).map((item) => (
  124. <NovelItem
  125. key={item.uri}
  126. slug={item.uri}
  127. img={item.img}
  128. name={item.name}
  129. />
  130. ))}
  131. </ul>
  132. </main>
  133. );
  134. };
  135. export const getServerSideProps: GetServerSideProps<
  136. { fallback: { [key: string]: any } },
  137. { genre: string }
  138. > = async ({ params }) => {
  139. const key = params?.genre ? `/api/genre/${params.genre}` : `/api/list`;
  140. const data = await get(key);
  141. return {
  142. props: {
  143. fallback: {
  144. [key]: data,
  145. },
  146. },
  147. };
  148. };
  149. export default Genre;